using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using RSSRadio.Model;

namespace RSSRadio
{
	/// <summary>
	/// Downloads a podcast and updates podcast library.
	/// </summary>
	/// <remarks>
	/// Replaces PodcastDownloader, which is not compatible with .Net CF.
	/// </remarks>
	public class PodcastDownloader2
	{
		Uri Url;
		string DestFileName;
		//public PodcastInfo Info;
        public Subscription feed;
        public EpisodeDetails episode;
		public int PercentCompleted;
		public int KBps;
		public int MegsReceived;
		public int TotalMegs;
		public int SecondsRemaining;
		DateTime StartTime;
		public bool WantToCancel;
		public bool DownloadFailed;
		public bool IsCanceled;
        public bool Waiting;


        public PodcastDownloader2(Subscription feed, EpisodeDetails episode)
		{
			Url = episode.Url;
            DestFileName = MediaFileUtil.getFilenameForEpisode(episode);
            if (File.Exists(DestFileName))
            {
                throw new Exception("File Already Exists.");
            }

            this.feed = feed;
            this.episode = episode;
            PercentCompleted = 0;
			StartTime = DateTime.Now;
			KBps = 0;
			WantToCancel = false;
			TotalMegs = 0;
			SecondsRemaining = 0;
            Waiting = true;
		}

		public void Download()
		{
			Download(null);
		}

		public void DownloadAsync()
		{
			Globals.ActivePodcastDownloaders.Add(this);
            feed.SubscriptionEpisodeUpdated(feed.URL, this.Url.ToString(), this.episode.Date, EpisodeState.PendingDownload);
            Globals.ThreadPool.QueueUserWorkItem(new WaitCallback(Download));
		}

		private void Download(object o)
		{
            feed.SubscriptionEpisodeUpdated(feed.URL, this.Url.ToString(), this.episode.Date, EpisodeState.DownloadStarted);
            HttpWebResponse httpResponse = null;
            Waiting = false;
            try
            {
                HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(Url);
                httpResponse = (HttpWebResponse)httpRequest.GetResponse();
                Stream responseStream = httpResponse.GetResponseStream();
                this.TotalMegs = Convert.ToInt32(httpResponse.ContentLength / 1048576);
                WriteStreamToFile(responseStream, DestFileName, httpResponse.ContentLength);

                if (WantToCancel)
                {
                    File.Delete(DestFileName);
                    IsCanceled = true;
                    feed.SubscriptionEpisodeUpdated(feed.URL, this.Url.ToString(), this.episode.Date, EpisodeState.DownloadCancel);
                }
                else
                {
                    PodcastInfo Info = new PodcastInfo(episode, feed.Title, feed.URL,
                        DestFileName);
                    Globals.DownloadedPodcasts.AddItem(Info);
                    Globals.DownloadedPodcasts.SaveToXml();

                    feed.SubscriptionEpisodeUpdated(feed.URL, this.Url.ToString(), this.episode.Date, EpisodeState.DownloadComplete);
                }
            }
            catch (ThreadAbortException) { /* DO nothing. This occurs on app close when background threads die */ }
            catch (Exception ex)
            {

                DownloadFailed = true;
                MessageBox.Show(String.Format("Download failed: {0}", ex.Message ));
                Globals.logError(ex);
            }
			finally
			{
				if (httpResponse != null)
					httpResponse.Close();
			}

            /* Suppress message boxes on success
			if (!WantToCancel && !DownloadFailed)
				MessageBox.Show("Download completed.", "");
            */

			lock (Globals.ActivePodcastDownloaders)
				Globals.ActivePodcastDownloaders.Remove(this);
		}

		private void WriteStreamToFile(Stream stream, string fileName, long streamLength)
		{
            try
            {
                FileStream output = new FileStream(fileName, FileMode.Create);
                try
                {
                    //the combination of the below params makes trafde off between 
                    //speed limit and CPU load
                    int buffSizeKB = 32; //downlaod buffer size in kb
                    int sleep = 33; //Sleep time in millis 
                    byte[] buffer = new byte[buffSizeKB * 1024];
                    int read;
                    long totalRead = 0;

                    while (!WantToCancel && (read = stream.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        output.Write(buffer, 0, read);
                        totalRead += read;
                        this.PercentCompleted = Convert.ToInt32((totalRead * 100) / streamLength);
                        TimeSpan elapsed = DateTime.Now - StartTime;
                        if (elapsed.TotalSeconds > 0)
                            this.KBps = Convert.ToInt32((totalRead / 1024) / elapsed.TotalSeconds);
                        if (this.KBps > 0)
                            this.SecondsRemaining = Convert.ToInt32(((streamLength - totalRead) / 1024)) / this.KBps;
                        this.MegsReceived = Convert.ToInt32(totalRead / 1048576);

                        //Yield to other threads and the system to allow normal system performance during downloads
                        Thread.Sleep(sleep);
                        
                        //Reset system power off timer to prevent power off during download 
                        //TODO: only call once every 40-50 seconds not on every loop
                        WinApi.SystemIdleTimerReset();
                    }
                }
                finally
                {
                    if (output != null)
                    {
                        output.Flush();
                        output.Close();
                        ((IDisposable)output).Dispose();

                    }
                }
            }
            catch (Exception ex)
            {
                //try force clean up and re-throw
                try
                {
                    File.Delete(DestFileName);
                }
                catch (Exception ) { }
                throw ex;
            }
		}
	}
}
